package com.bupt.godxian.charging.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.bupt.godxian.charging.config.CommonResponse;
import com.bupt.godxian.charging.entity.*;
import com.bupt.godxian.charging.mapper.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static org.hibernate.action.internal.BulkOperationCleanupAction.schedule;


@Service
public class UserService {
    private final UserMapper userMapper;

    @Autowired
    private ChargeRequestMapper chargeRequestMapper;
    @Autowired
    private ChargeAreaMapper chargeAreaMapper;

    @Autowired
    private ChargeWaitAreaMapper chargeWaitAreaMapper;

    @Autowired
    private WaitAreaMapper waitAreaMapper;

    @Autowired
    private ChargerMapper chargerMapper;

    @Autowired
    private WaitQueueMapper waitQueueMapper;

    @Autowired
    private ChargeRecordMapper chargeRecordMapper;

    @Autowired
    private ScheduleService scheduleService;
    @Autowired
    public UserService(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    public void insert(User user) {
        userMapper.insert(user);
    }

    public void update(User user) {
        userMapper.update(user);
    }

    public void delete(Integer id) {
        userMapper.delete(id);
    }

    public User findById(Integer id) {
        return userMapper.findById(id);
    }

    // 调用数据访问层获取用户信息
    public User findByUsername(String username) {
        return userMapper.findByUsername(username);
    }

    public Integer AdminById(Integer id){return userMapper.AdminById(id);}
    //一些路由操作--------------------------------------------------------------------------------------------------------
    //修改充电请求
    public CommonResponse edit_charging_request(User user,String chargeMode,Double requireAmount){
        boolean success=true;
        String error_msg=null;
        // 判断是否可以修改
        ChargeRequest record = chargeRequestMapper.selectOne(new QueryWrapper<ChargeRequest>()
                .eq("user_id", user.getId())
                .eq("state", 1));
        //chargeRequestMapper.findByUserIdAndState(user.getId(), 1);

        // 存在还在等候区的
        if (record != null) {
            // 如果充电模式有修改则放到队列最后,这里如何实现？需要重写吗
            String chargeId = record.getChargeId();
            if (!record.getChargeMode().equals(chargeMode)) {
                // WaitArea 等候区相关处理
                waitAreaMapper.delete(new QueryWrapper<WaitArea>().eq("request_id", record.getId()));
                waitAreaMapper.insert(new WaitArea(String.valueOf(record.getId()), chargeMode));

                // 生成charge_id,加入队列
                Long hisFrontCars = waitQueueMapper.selectCount(new QueryWrapper<WaitQueue>().eq("type", chargeMode));
                if (hisFrontCars == 0) {
                    chargeId = chargeMode + "1";
                } else {
                    List<ChargeRequest> resRaw = chargeRequestMapper.selectList(new QueryWrapper<ChargeRequest>().eq("charge_mode",chargeMode));
                    //                            findByChargeMode(chargeMode);
                    int res = resRaw.stream()
                            .mapToInt(c -> Integer.parseInt(c.getChargeId().substring(1)))
                            .max()
                            .orElse(0);
                    chargeId = chargeMode + (res + 1);
                }
                waitQueueMapper.update(new WaitQueue(),
                        new UpdateWrapper<WaitQueue>()
                                .set("charge_id", chargeId)
                                .set("type", chargeMode)
                                .eq("charge_id", record.getChargeId()));

                scheduleService.schedule(2, Long.valueOf(record.getId()),null,null,false,false);
            }

            // 修改后数据写入数据库
            chargeRequestMapper.update(null,
                    new UpdateWrapper<ChargeRequest>()
                            .set("charge_mode", chargeMode)
                            .set("require_amount", requireAmount)
                            .set("charge_id", chargeId)
                            .eq("id", record.getId()));

        } else {
            success=false;
            error_msg="修改失败，车辆不在等候区。";
        }
        if (success){
            return new CommonResponse(0, "Success");
        }else return new CommonResponse(-1, error_msg);

    }


    //取消充电请求
    public CommonResponse end_charging_request(User user) {
        boolean success = false;
        String error_msg = null;
        //record是从充电请求中获取的
        ChargeRequest record = chargeRequestMapper.getUndoChargeRequestByUserId(user.getId());

        if (record == null) {
            success = false;
            error_msg = "该用户没有充电请求";
        } else {

            //生成详单
//            Timer timer = new Timer();
            LocalDateTime now = LocalDateTime.now();

            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            String createTime = now.format(formatter);

            String orderId = now.format(DateTimeFormatter.ofPattern("yyyyMMdd"));

            ChargeRecord chargeRecord = new ChargeRecord();
            //获得session.query(ChargeRecord).count() + 1
            Integer recordId=chargeRecordMapper.get_record_id();
//            Long recordId = chargeRecordMapper.selectCount(new QueryWrapper<ChargeRecordMapper>().eq("type", chargeRecord));
//            QueryWrapper<ChargeRecordMapper> queryWrapper = new QueryWrapper<ChargeRecordMapper>().eq("type", chargeRecord.getType());
//            Long recordId = chargeRecordMapper.selectCount(queryWrapper);

            // 如果当前不在充电:直接生成充电详单
            if (record.getState() != 3) {
                chargeRecord.setId(String.valueOf(recordId));
                chargeRecord.setOrderId(orderId);
                chargeRecord.setCreateTime(createTime);
                chargeRecord.setChargedAmount(0.00);
                chargeRecord.setChargedTime(0.00);
                chargeRecord.setBeginTime("0.00");
                chargeRecord.setEndTime("0.00");
                chargeRecord.setChargingCost(0.00);
                chargeRecord.setServiceCost(0.00);
                chargeRecord.setTotalCost(0.00);
                chargeRecord.setPileId(record.getChargePileId());
                chargeRecord.setUserId(String.valueOf(user.getId()));

                chargeRecordMapper.insert(chargeRecord);

            } else {
                // 如果当前正在充电，计算后再创建充电详单 question：算法尚未测试
                LocalDateTime beginTime = LocalDateTime.ofEpochSecond(record.getStartTime().longValue(), 0, ZoneOffset.UTC);
                LocalDateTime endTime = now;
                long chargedTime = Duration.between(beginTime, endTime).getSeconds();  // 充电时长

                double rate = (record.getChargeMode().equals("F")) ? 30 : 10;
                double chargedAmount = Double.parseDouble(String.format("%.2f", chargedTime / 3600.0 * rate));  // 充电量
                double serviceCost = Double.parseDouble(String.format("%.2f", 0.8 * chargedAmount));  // 服务费用

                // 计算充电费用
                // 将一天分成6个时间区域，只考虑24h内充完电的情况
                // 07：00 - 10：00  1  平时  0.7元/度
                // 10：00 - 15：00  2  峰时  1.0元/度
                // 15：00 - 18：00  3  平时  0.7元/度
                // 18：00 - 21：00  4  峰时  1.0元/度
                // 21：00 - 23：00  5  平时  0.7元/度
                // 23：00 - 07：00  6  谷时  0.4元/度
                int[] clocks = {7, 10, 15, 18, 21, 23, 31};
                double[] fees = {0.7, 1.0, 0.7, 1.0, 0.7, 0.4};

                int beginTimeZone = 0;
                int endTimeZone = 0;

                for (int i = 0; i < clocks.length + 1; i++) {
                    if (i == 6 || (clocks[i] <= beginTime.getHour() && beginTime.getHour() < clocks[(i + 1) % clocks.length])) {
                        beginTimeZone = i + 1;
                        break;
                    }
                }

                for (int i = 0; i < clocks.length; i++) {
                    if (i == 6 || (clocks[i] <= endTime.getHour() && endTime.getHour() < clocks[(i + 1) % clocks.length])) {
                        endTimeZone = i + 1;
                        break;
                    }
                }

                beginTimeZone = beginTimeZone % clocks.length;
                endTimeZone = endTimeZone % clocks.length;

                double chargingCost;

                if (beginTimeZone == endTimeZone) {
                    chargingCost = Double.parseDouble(String.format("%.2f", chargedAmount * fees[beginTimeZone - 1]));
                } else {
                    long diffTime1 = (clocks[beginTimeZone] - (beginTime.getHour() >= 7 ? beginTime.getHour() : (beginTime.getHour() + 24)) - 1) * 3600 +
                            (60 - beginTime.getMinute()) * 60 + 60 - beginTime.getSecond();
                    long diffTime2 = ((endTime.getHour() >= 7 ? endTime.getHour() : (endTime.getHour() + 24)) - clocks[(endTimeZone - 1) % clocks.length]) * 3600 +
                            endTime.getMinute() * 60 + endTime.getSecond();

                    List<Integer> zones = new ArrayList<>();

                    if (beginTimeZone < endTimeZone) {
                        for (int i = beginTimeZone; i <= endTimeZone; i++) {
                            zones.add(i);
                        }
                    } else {
                        for (int i = beginTimeZone; i < clocks.length; i++) {
                            zones.add(i);
                        }

                        for (int i = 1; i <= endTimeZone; i++) {
                            zones.add(i);
                        }
                    }

                    chargingCost = 0.0;

                    for (int zone : zones) {
                        if (zone == beginTimeZone) {
                            chargingCost += diffTime1 / 3600.0 * rate * fees[zone - 1];
                        } else if (zone == endTimeZone) {
                            chargingCost += diffTime2 / 3600.0 * rate * fees[zone - 1];
                        } else {
                            chargingCost += (clocks[zone] - clocks[zone - 1]) * rate * fees[zone - 1];
                        }
                    }

                    chargingCost = Double.parseDouble(String.format("%.2f", chargingCost));
                }

                double totalCost = serviceCost + chargingCost;
                //添加充电详单
                chargeRecord.setId(String.valueOf(recordId));
                chargeRecord.setOrderId(orderId);
                chargeRecord.setCreateTime(createTime);
                chargeRecord.setChargedAmount(Double.valueOf(String.format("%.2f", chargedAmount)));
                chargeRecord.setChargedTime(Double.valueOf(String.valueOf(chargedTime)));
                chargeRecord.setBeginTime(beginTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                chargeRecord.setEndTime(endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                chargeRecord.setChargingCost(Double.valueOf(String.format("%.2f", chargingCost)));
                chargeRecord.setServiceCost(Double.valueOf(String.format("%.2f", serviceCost)));
                chargeRecord.setTotalCost(Double.valueOf(String.format("%.2f", totalCost)));
                chargeRecord.setPileId(record.getChargePileId());
                chargeRecord.setUserId(String.valueOf(user.getId()));

                chargeRecordMapper.insert(chargeRecord);

                //更新充电桩信息
//                Charger charger=chargerMapper.selectOnebyId(chargeRecord.getPileId());
                chargerMapper.updatecharger(chargedTime,chargedAmount,chargeRecord.getPileId());




                chargerMapper.updateCumulativeChargingAmount(Long.valueOf(chargeRecord.getPileId()), String.valueOf(Float.parseFloat(chargerMapper.selectCumulativeChargingAmount(Long.valueOf(chargeRecord.getPileId()))) + chargeRecord.getChargedAmount()));

                BigDecimal chargedTimeBigDecimal = BigDecimal.valueOf(chargeRecord.getChargedTime());
                chargerMapper.updateCumulativeChargingTime( chargeRecord.getPileId(),
                        chargedTimeBigDecimal.add(chargerMapper.selectOne(new QueryWrapper<Charger>().eq("id",chargeRecord.getPileId())).getCumulativeChargingTime()));

                chargerMapper.updateCumulativeUsageTimes(Long.valueOf(chargeRecord.getPileId()), chargeRecord.getChargedTime() + chargerMapper.selectCumulativeUsageTimes(Long.valueOf(chargeRecord.getPileId())));

            }

            success = true;
            try {
                // python代码中这里有个调度函数，不知道怎么用，留下了
                // 希望我用的是对的
                scheduleService.schedule(1, Long.valueOf(record.getId()), null, null, false, false);

                chargeRequestMapper.updateState(Long.valueOf(record.getId()), 0);
                waitAreaMapper.deleteById(String.valueOf(record.getId()));
                waitQueueMapper.deleteById(record.getChargeId());
                chargeAreaMapper.deleteById(String.valueOf(record.getId()));
//            chargeAreaMapper.d
                chargeWaitAreaMapper.deleteById(String.valueOf(record.getId()));
            }catch (Exception e){
                success=false;
                error_msg=e.getMessage();
            }



        }

        if (success) {
            return new CommonResponse(0, "Success");
        } else
            return new CommonResponse(-1, error_msg);
    }
    //查看账单
    public Map<String, Object> query_bill(User user){
//        boolean success = true;
        String error_msg = null;
        try {
            List<Bill> bill_list_old = new ArrayList<>();

            bill_list_old = chargeRecordMapper.findBillByUserId(Math.toIntExact(user.getId()));

            List<Bill> bill_list = bill_list_old.stream()
                    .map(Bill -> new Bill(
                            Bill.getBillId(),
                            Bill.getCreateTime(),
                            Bill.getChargedAmount(),
                            Bill.getChargedTime(),
                            Bill.getBeginTime()
                    ))
                    .collect(Collectors.toList());
            Map<String, Object> responseData = new HashMap<>();
            responseData.put("code",0);
            responseData.put("message", "Success");
            responseData.put("data", bill_list);
            return responseData;
        }catch (Exception e){
            Map<String, Object> responseData = new HashMap<>();
            responseData.put("code",0);
            responseData.put("message", e.getMessage());
            responseData.put("data", null);
            return  responseData;
        }

//
//        if (success){
//            return new CommonResponse(0, "Success", (Map<String, Object>) bill_list);
//        }else return new CommonResponse(-1, error_msg, (Map<String, Object>) null);


    }
    //查看充电详单
    public Map<String, Object> query_order_detail(User user){
//        boolean success = false;
        String error_msg = null;
        try {
        List<ChargeRecord> order_list_old = new ArrayList<>();

        order_list_old = chargeRecordMapper.findByUserId(Math.toIntExact(user.getId()));

        List<Order> order_list = order_list_old.stream()
                .map(chargeRecord -> new Order(
                        chargeRecord.getOrderId(),
                        chargeRecord.getCreateTime(),
                        chargeRecord.getChargedAmount(),
                        chargeRecord.getChargedTime(),
                        chargeRecord.getBeginTime(),
                        chargeRecord.getEndTime(),
                        chargeRecord.getChargingCost(),
                        chargeRecord.getServiceCost(),
                        chargeRecord.getTotalCost(),
                        chargeRecord.getPileId()
                ))
                .collect(Collectors.toList());
            Map<String, Object> responseData = new HashMap<>();
            responseData.put("code",0);
            responseData.put("message", "Success");
            responseData.put("data", order_list);
            return responseData;
        }catch (Exception e){
            Map<String, Object> responseData = new HashMap<>();
            responseData.put("code",0);
            responseData.put("message", e.getMessage());
            responseData.put("data", null);
            return  responseData;
        }
//        if (success){
//            return new CommonResponse(0, "Success", (Map<String, Object>) order_list);
//        }else return new CommonResponse(-1, error_msg, (Map<String, Object>) null);

    }
    //预览排队情况, String chargeMode, Double requireAmount
    public CommonResponse preview_queue(User user) throws JsonProcessingException {
//        boolean success = false;
        String error_msg = null;

        try {
            QueryWrapper<ChargeRequest> requestQueryWrapper = new QueryWrapper<>();
            requestQueryWrapper.eq("user_id", user.getId()).ne("state", 0);
            ChargeRequest record = chargeRequestMapper.selectOne(requestQueryWrapper);

            String chargeId = null;
            String curState = null;
            String place = null;
            Long queueLen = 0L;

            // 若存在还未结束的充电请求
            if (record != null) {
                chargeId = record.getChargeId();
                String[] info = {"NOTCHARGING", "WAITINGSTAGE1", "WAITINGSTAGE2", "CHARGING", "CHANGEMODEREQUEUE", "FAULTREQUEUE"};
                curState = info[record.getState()];

                // 算前方车辆
                if (record.getState() == 1) {
                    place = "WAITINGPLACE";
                } else if (record.getState() == 2 || record.getState() == 3 || record.getState() == 5) {
                    place = record.getChargePileId();
                } else {
                    place = null;
                }


                if (record.getState() == 1 || record.getState() == 4) {
                    QueryWrapper<WaitQueue> waitQueueQueryWrapper = new QueryWrapper<>();
                    waitQueueQueryWrapper.eq("type", record.getChargeMode()).lt("charge_id", record.getChargeId());
                    int numWait = chargeRequestMapper.getHisFrontCarsCount(String.valueOf(waitQueueQueryWrapper));
//                QueryWrapper<ChargeArea> chargeAreaQueryWrapper = new QueryWrapper<>();
                    //空值待处理吗？
                    int numChargeWait = waitAreaMapper.selectWaitAreaCarnum();

                    queueLen = (long) (numWait + numChargeWait);
                } else if (record.getState() == 2) {
                    QueryWrapper<Charger> chargerQueryWrapper = new QueryWrapper<>();
                    chargerQueryWrapper.eq("id", record.getChargePileId());
                    Charger chargePile = chargerMapper.selectOne(chargerQueryWrapper);

                    QueryWrapper<ChargeArea> chargeAreaQueryWrapper = new QueryWrapper<>();
                    chargeAreaQueryWrapper.eq("pile_id", chargePile.getId()).lt("request_id", record.getId());
                    queueLen = chargeAreaMapper.selectCount(chargeAreaQueryWrapper);
                } else if (record.getState() == 3) {
                    queueLen = 0L;
                } else if (record.getState() == 5) {
                    queueLen = 1L;
                } else {
                    queueLen = 0L;
                }
            } else {
                curState = "NOTCHARGING";
                chargeId = null;
                queueLen = 0L;
                place = null;
            }

//            ObjectMapper objectMapper = new ObjectMapper();

            Map<String, Object> data = new HashMap<>();
            data.put("charge_id", chargeId);
            data.put("queue_len", queueLen);
            data.put("cur_state", curState);
            data.put("place", place);

            return new CommonResponse(0, "Success", data);
        }catch (Exception e){
            return new CommonResponse(-1, error_msg, (Map<String, Object>) null);
        }


    }
}
